﻿using System;
using System.Collections.Generic;
using System.Numerics;
using System.Reflection;
using System.Windows.Media;
using NodeBasedSkillEditor.Model;
using NodeBasedSkillEditor.ViewModel;


namespace NodeBasedSkillEditor
{
    public static partial class DAGHelper
    {

        public static Color DAGBackgroundColor = Color.FromRgb(40, 40, 40);
        public static Color DAGSubGridColor = Color.FromRgb(48, 48, 48);
        public static Color DAGMainGridColor = Color.FromRgb(56, 56, 56);
        public static Color DAGGridCrossColor = Color.FromRgb(88, 88, 88);

        public static Color UnknownDataColor = Colors.Black;
        public static Color FlowDataColor = Color.FromRgb(128, 128, 128);
        public static Color BooleanDataColor = Color.FromRgb(200, 100, 40);
        public static Color IntegerDataColor = Color.FromRgb(15, 155, 120);
        public static Color FloatDataColor = Color.FromRgb(135, 180, 56);
        public static Color Vector2DataColor = Color.FromRgb(180, 180, 0);
        public static Color Vector3DataColor = Color.FromRgb(200, 160, 0);
        public static Color StringDataColor = Color.FromRgb(0, 120, 180);
        public static Color EnumDataColor = Color.FromRgb(105, 85, 220);


        #region Node Setup
        public const string CategoryForAllNodes = "All Nodes";

        public static ColorHandler Color_Math = new ColorHandler("#232a39");

        public static ColorHandler Color_Convert = new ColorHandler("#2e2735");

        public static ColorHandler Color_Data = new ColorHandler("#273334");

        public static ColorHandler Color_Execution = new ColorHandler("#303030");

        public static ColorHandler Color_Entry = new ColorHandler("#726028");

        public static ColorHandler Color_List = new ColorHandler("#3c3932");

        public static Color GetNodeColorByCategory(string categoryName)
        {
            switch (categoryName)
            {
                case CateName_Math:
                case CateName_MathLogic:
                case CateName_MathTri:
                case CateName_MathVec:
                    return Color_Math;

                case CateName_Convert: return Color_Convert;
                case CateName_Data: return Color_Data;

                case CateName_Execution:
                case CateName_GameEvent:
                    return Color_Execution;

                case CateName_Entry: return Color_Entry;
                case CateName_List: return Color_List;

                default: return Colors.DimGray;
            }
        }

        #endregion

        public static SortedDictionary<string, Type> NodeTypeRepository { get; private set; }

        private static Dictionary<string, SortedDictionary<string, Type>> _allNodeTypeDictionary;
        public static Dictionary<string, SortedDictionary<string, Type>> AllNodeTypeDictionary {
            get {
                if (_allNodeTypeDictionary == null)
                {
                    _allNodeTypeDictionary = new Dictionary<string, SortedDictionary<string, Type>>();
                    NodeTypeRepository = new SortedDictionary<string, Type>();

                    var allNodeCate = new SortedDictionary<string, Type>();
                    _allNodeTypeDictionary.Add(CategoryForAllNodes, allNodeCate);

                    var allTypes = Assembly.GetExecutingAssembly().GetTypes();
                    foreach (var t in allTypes)
                    {
                        var attr = t.GetCustomAttribute<NodeAttribute>();
                        if (t.Namespace == "NodeBasedSkillEditor.Model.Node" && t.IsSubclassOf(typeof(NodeData)))
                        {
                            var category = "No Category";
                            var name = t.Name;
                            if (attr != null)
                            {
                                category = attr.Category;
                                name = attr.Name;
                            }

                            if (!_allNodeTypeDictionary.TryGetValue(category, out SortedDictionary<string, Type> types))
                            {
                                types = new SortedDictionary<string, Type>();
                                _allNodeTypeDictionary.Add(category, types);
                            }
                            types.Add(name, t);
                            allNodeCate.Add(name, t);
                            NodeTypeRepository.Add(name, t);
                        }
                    }
                }
                return _allNodeTypeDictionary;
            }
        }

        public static Color GetColorFromDataType(Type type)
        {
            if (type == typeof(FlowPort)) return FlowDataColor;
            else if (type == typeof(bool)) return BooleanDataColor;
            else if (type == typeof(int)) return IntegerDataColor;
            else if (type == typeof(float)) return FloatDataColor;
            else if (type == typeof(Vector2)) return Vector2DataColor;
            else if (type == typeof(Vector3)) return Vector3DataColor;
            else if (type == typeof(string)) return StringDataColor;
            else if (type.IsEnum) return EnumDataColor;
            else return UnknownDataColor;
        }

        public static bool ShouldPortBeVisibleInInspector(VM_Port portVM)
        {
            return portVM.IOType == EPortIOType.Input
                && portVM.BindPortData.GetType().IsSubclassOf(typeof(ValuePort));
        }

        public static bool ShouldPortBeMultiConnectable(BasePort portData, EPortIOType ioType)
        {
            // Flow端口多入单出
            if (portData.GetType() == typeof(FlowPort))
            {
                return ioType == EPortIOType.Input;
            }
            // 数值端口单入多出
            else
            {
                return ioType == EPortIOType.Output;
            }
        }

        public static bool CanPortRelayContext(bool isList, EPortIOType ioType, EPortContextMode contextMode)
        {
            if (!isList)
            {
                return false;
            }
            switch (ioType)
            {
                case EPortIOType.Unknown:
                    return false;
                case EPortIOType.Input:
                    return contextMode != EPortContextMode.Isolate;
                case EPortIOType.Output:
                    return contextMode == EPortContextMode.Relay;
                default:
                    return false;
            }
        }

        public static bool CanPortRelayContext(VM_Port port)
        {
            return CanPortRelayContext(port.IsListPort, port.IOType, port.ContextMode);
        }

        public static bool CanPortOutputContext(bool isListPort, EPortIOType ioType, EPortContextMode contextMode)
        {
            return isListPort && ioType == EPortIOType.Output && contextMode != EPortContextMode.Relay;
        }

        public static bool CanPortOutputContext(VM_Port port)
        {
            return CanPortOutputContext(port.IsListPort, port.IOType, port.ContextMode);
        }

        public static uint GetPortOutputContext(VM_Port port)
        {
            if (port.IOType == EPortIOType.Output)
            {
                if (port.ContextMode == EPortContextMode.Node)
                {
                    return port.ParentNode.LocalId;
                }
                else if (port.ContextMode == EPortContextMode.Isolate)
                {
                    return port.Id;
                }
            }
            return EmptyContextId;
        }

        public static Type GetPortTypeViaDataType(Type dataType)
        {
            if (dataType == typeof(bool)) return typeof(BooleanPort);
            if (dataType == typeof(int)) return typeof(IntegerPort);
            if (dataType == typeof(float)) return typeof(FloatPort);
            if (dataType == typeof(Vector2)) return typeof(Vector2Port);
            if (dataType == typeof(Vector3)) return typeof(Vector3Port);
            if (dataType == typeof(string)) return typeof(StringPort);
            if (dataType == typeof(Enum)) return typeof(ValuePort); // 这里发生了端口类型退化
            if (dataType.IsEnum)
            {
                var genericEnumPortType = typeof(EnumPort<>);
                Type[] genericEnum = { dataType };
                return genericEnumPortType.MakeGenericType(genericEnum);
            }
            return null;
        }

        /// <summary>
        /// 当上下文彼此一致、或其中之一没有上下文时可以兼容
        /// </summary>
        /// <param name="context1"></param>
        /// <param name="context2"></param>
        /// <returns></returns>
        public static bool AreContextsCompatible(uint context1, uint context2)
        {
            return context1 == context2 || context1 == EmptyContextId || context2 == EmptyContextId;
        }

    }
}
